#pragma once
#include <iostream>
#include <unistd.h>
#include <cstdarg>
#include <ctime>
#include <string>
#include <fstream>
#include <cstring>
#include "LockGuard.hpp"
namespace log_ns
{
    enum grade
    {
        DEBUG,
        INFO,
        WARRING,
        ERROR,
        FATAL
    };

    class logmessage
    {

    public:
        int _grade;
        pid_t _pid;
        std::string _filename;
        int _filenumber;
        std::string _current_time;
        std::string _message;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2
    std::string glogfile = "./log.txt";
    std::string glogdev = "stderr";


    class Log
    {
    private:
        void ShowToScreen(const std::string &show)
        {
            //std::cout << show;
            std::cerr << show;
        }
        void ShowToFile(const std::string &show)
        {
            std::ofstream out("log.txt", std::ios::app);

            if (!out.is_open())
            {
                return;
            }
            out << show;
            out.close();
        }

        std::string GetCurrentTime()
        {
            time_t now = time(nullptr);
            struct tm *str_now = localtime(&now);
            char buffer[1024];
            snprintf(buffer, sizeof(buffer),
                     "%d-%02d-%02d %02d:%02d:%02d",
                     str_now->tm_year + 1900,
                     str_now->tm_mon + 1,
                     str_now->tm_mday,
                     str_now->tm_hour,
                     str_now->tm_min,
                     str_now->tm_sec);
            return buffer;
        }
        std::string GetGradeString(int greade)
        {
            switch (greade)
            {
            case 0:
                return "DEBUG";
            case 1:
                return "INFO";
            case 2:
                return "WARRING";
            case 3:
                return "ERROR";
            case 4:
                return "FATAL";
            default:
                return "UNKONW";
            }
        }
        std::string GetFormatMessage(logmessage *logmes)
        {

            char buffer[2048];
            snprintf(buffer, sizeof(buffer),
                     "[%s][%d][%s][%d][%s] %s",
                     GetGradeString(logmes->_grade).c_str(),
                     logmes->_pid,
                     logmes->_filename.c_str(),
                     logmes->_filenumber,
                     logmes->_current_time.c_str(),
                     logmes->_message.c_str());
            return buffer;
        }

        void ShowLog(logmessage *logmes)
        {

            std::string final_message = GetFormatMessage(logmes);
            switch (_type)
            {
            case SCREEN_TYPE:
                ShowToScreen(final_message);
                break;
            case FILE_TYPE:
                ShowToFile(final_message);
                break;
            }
        }

    public:
        Log(int type = SCREEN_TYPE, std::string logfile = glogfile) : _type(type), _logfile(logfile)
        {
        }
        void Enable(int type)
        {
            _type = type;
        }
        void Logmessage(int grade, std::string filename, int filenumber, const char *format...)
        {

            LockGuard();
            logmessage log_message;

            log_message._grade = grade;
            log_message._filename = filename;
            log_message._filenumber = filenumber;
            log_message._pid = getpid();

            va_list ap;
            va_start(ap, format);
            char log_info[1024];
            vsnprintf(log_info, sizeof(log_info), format, ap);
            log_message._message = log_info;

            log_message._current_time = GetCurrentTime();

            ShowLog(&log_message);
        }

    private:
        int _type;
        std::string _logfile;
    };

    Log lg;
#define EnableScreen()          \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)
#define EnableFile()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
#define LOG(Gread, Format, ...)                                          \
    do                                                                   \
    {                                                                    \
        lg.Logmessage(Gread, __FILE__, __LINE__, Format, ##__VA_ARGS__); \
    } while (0)
};